//	mpsf.looppts.c

#include "mpsf.looppts.h"

t_class *lp_class;
t_symbol *_sym_float32;
//t_symbol *_sym_float64;
t_symbol *_sym_dumpout;

//#define calcoffset(x,y) ((long)(&(((x *)0L)->y)))

#pragma warning(push)
#pragma warning(disable: 4311)	// warning C4311: 'type cast' : pointer truncation from 't_ms *__w64 ' to 'long'
__declspec(dllexport) void main(void)
{
	lp_class = class_new("mpsf.looppts", (method)lp_new, (method)lp_free,
							(long)sizeof(t_lppts), (method)0L, A_GIMME, 0L);
	class_obexoffset_set(lp_class, calcoffset(t_lppts, _obex));
	//class_obexoffset_set(lp_class, (long)(&((t_lppts*)NULL)->_obex));
	class_addmethod(lp_class, (method)lp_loopstart, "loopstart", A_FLOAT, 0L);
	class_addmethod(lp_class, (method)lp_loopend, "loopend", A_FLOAT, 0L);
	class_addmethod(lp_class, (method)lp_loopcenter, "loopcenter", A_FLOAT, 0L);
	class_addmethod(lp_class, (method)lp_looplength, "looplength", A_FLOAT, 0L);
	class_addmethod(lp_class, (method)lp_samplength, "samplength", A_FLOAT, 0L);
	class_addmethod(lp_class, (method)lp_float, "float", A_FLOAT, 0L);
	class_addmethod(lp_class, (method)lp_int, "int", A_LONG, 0L);
	class_addmethod(lp_class, (method)lp_bang, "bang", 0L);
	_sym_float32 = gensym("float32");
	//_sym_float64 = gensym("float64");
	_sym_dumpout = gensym("dumpout");
	class_addattr(lp_class, attr_offset_new("loopstart", _sym_float32, 0L, (method)0L,
							(method)lp_setloopstart, calcoffset(t_lppts, _loopstart)));
	class_addattr(lp_class, attr_offset_new("loopsend", _sym_float32, 0L, (method)0L,
							(method)lp_setloopend, calcoffset(t_lppts, _loopend)));
	class_addattr(lp_class, attr_offset_new("loopcenter", _sym_float32, 0L, (method)0L,
							(method)lp_setloopcenter, calcoffset(t_lppts, _loopcenter)));
	class_addattr(lp_class, attr_offset_new("looplength", _sym_float32, 0L, (method)0L,
							(method)lp_setlooplength, calcoffset(t_lppts, _looplength)));
	class_addattr(lp_class, attr_offset_new("samplength", _sym_float32, 0L, (method)0L,
							(method)lp_setsamplength, calcoffset(t_lppts, _samplength)));
	class_addmethod(lp_class, (method)object_obex_dumpout, "dumpout", A_CANT, 0L);
	class_addmethod(lp_class, (method)object_obex_quickref, "quickref", A_CANT, 0L);
	class_register(CLASS_BOX, lp_class);
}
#pragma warning(pop)

void* lp_new(t_symbol *sym, long argc, t_atom *argv)
{
	t_lppts *x;
	x = (t_lppts*)object_alloc(lp_class);
	if(x == NULL)
		return NULL;
	x->_loopstart = (t_ms)0.0;
	x->_loopend = (t_ms)0.0;
	x->_loopcenter = (t_ms)0.0;
	x->_looplength = (t_ms)0.0;
	x->_samplength = (t_ms)0.0;
	attr_args_process(x, (short)argc, argv);
	object_obex_store(x, _sym_dumpout, outlet_new(x, NULL));
	x->_outlet[LP_OUTLET_LOOPLENGTH] = floatout(x);
	x->_outlet[LP_OUTLET_LOOPCENTER] = floatout(x);
	x->_outlet[LP_OUTLET_LOOPEND] = floatout(x);
	x->_outlet[LP_OUTLET_LOOPSTART] = floatout(x);
	x->_proxy[LP_PROXY_SAMPLENGTH] = proxy_new(x, LP_INLET_SAMPLENGTH, &x->_inletnumber);
	x->_proxy[LP_PROXY_LOOPLENGTH] = proxy_new(x, LP_INLET_LOOPLENGTH, &x->_inletnumber);
	x->_proxy[LP_PROXY_LOOPCENTER] = proxy_new(x, LP_INLET_LOOPCENTER, &x->_inletnumber);
	x->_proxy[LP_PROXY_LOOPEND] = proxy_new(x, LP_INLET_LOOPEND, &x->_inletnumber);
	return x;
}

void lp_free(t_lppts *x)
{
	freeobject(x->_proxy[LP_PROXY_SAMPLENGTH]);
	freeobject(x->_proxy[LP_PROXY_LOOPLENGTH]);
	freeobject(x->_proxy[LP_PROXY_LOOPCENTER]);
	freeobject(x->_proxy[LP_PROXY_LOOPEND]);
}

void lp_float(t_lppts *x, t_ms f)
{
	long i = proxy_getinlet((t_object*)x);
	switch(i)
	{
	case LP_INLET_LOOPSTART:	lp_loopstart(x, f);		break;
	case LP_INLET_LOOPEND:		lp_loopend(x, f);		break;
	case LP_INLET_LOOPCENTER:	lp_loopcenter(x, f);	break;
	case LP_INLET_LOOPLENGTH:	lp_looplength(x, f);	break;
	case LP_INLET_SAMPLENGTH:	lp_samplength(x, f);	break;
	default:
		error("float message with proxy_getinlet() = %ld", i);
	}
}

void lp_int(t_lppts *x, long n)
{
	long i = proxy_getinlet((t_object*)x);
	switch(i)
	{
	case LP_INLET_LOOPSTART:	lp_loopstart(x, (t_ms)n);	break;
	case LP_INLET_LOOPEND:		lp_loopend(x, (t_ms)n);		break;
	case LP_INLET_LOOPCENTER:	lp_loopcenter(x, (t_ms)n);	break;
	case LP_INLET_LOOPLENGTH:	lp_looplength(x, (t_ms)n);	break;
	case LP_INLET_SAMPLENGTH:	lp_samplength(x, (t_ms)n);	break;
	default:
		error("int message with proxy_getinlet() = %ld", i);
	}
}

void lp_bang(t_lppts *x)
{
	outlet_float(x->_outlet[LP_OUTLET_LOOPLENGTH], (double)x->_looplength);
	outlet_float(x->_outlet[LP_OUTLET_LOOPCENTER], (double)x->_loopcenter);
	outlet_float(x->_outlet[LP_OUTLET_LOOPEND], (double)x->_loopend);
	outlet_float(x->_outlet[LP_OUTLET_LOOPSTART], (double)x->_loopstart);
}

void lp_loopstart(t_lppts *x, t_ms f)
{
	if(f < (t_ms)0.0)
	{
		error("loopstart must be >= 0.0");
		f = 0.0;
	}
	if(f > x->_loopend)
	{
		error("loopstart must be <= loopend");
		f = x->_loopend;
	}
	x->_loopstart = f;
	x->_looplength = x->_loopend - f;
	if(x->_looplength >= x->_samplength)
	{
		x->_loopstart = 0.0;
		x->_loopend = x->_samplength;
		x->_loopcenter = x->_samplength / 2.0;
		x->_looplength = x->_samplength;
	} else {
		if(f < 0)
		{
			x->_loopstart = 0.0;
			x->_loopend = x->_looplength;
			x->_loopcenter = x->_looplength / 2.0;
		} else {
			x->_loopcenter = f + (x->_looplength / 2.0);
		}
	}
	lp_bang(x);
}

void lp_loopend(t_lppts *x, t_ms f)
{
	if(f < (t_ms)0.0)
	{
		error("loopend must be >= 0.0");
		f = 0.0;
	}
	if(f > x->_loopstart)
	{
		error("loopend must be >= loopstart");
		f = x->_loopstart;
	}
	x->_loopend = f;
	x->_looplength = f - x->_loopstart;
	if(x->_looplength >= x->_samplength)
	{
		x->_loopstart = 0.0;
		x->_loopend = x->_samplength;
		x->_loopcenter = x->_samplength / 2.0;
		x->_looplength = x->_samplength;
	} else {
		if(f > x->_samplength)
		{
			x->_loopend = x->_samplength;
			x->_loopstart = x->_loopend - x->_looplength;
			x->_loopcenter = x->_loopstart + (x->_looplength / 2.0);
		} else {
			x->_loopcenter = x->_loopstart + (x->_looplength / 2.0);
		}
	}
	lp_bang(x);
}

void lp_loopcenter(t_lppts *x, t_ms f)
{
	x->_loopcenter = f;
	x->_loopstart = f - x->_looplength / 2.0;
	x->_loopend = f + x->_looplength / 2.0;
	if(x->_loopstart < 0)
	{
		x->_loopstart = 0.0;
		x->_loopend = x->_looplength;
		x->_loopcenter = f / 2.0;
	} else if(x->_loopend > x->_samplength) {
		x->_loopend = x->_samplength;
		x->_loopstart = x->_loopend - x->_looplength;
		x->_loopcenter = x->_loopstart + (x->_looplength / 2.0);
	}
	lp_bang(x);
}

void lp_looplength(t_lppts *x, t_ms f)
{
	x->_looplength = f;
	if(f > x->_samplength)
	{
		x->_loopstart = 0.0;
		x->_loopend = x->_samplength;
		x->_looplength = x->_samplength;
		x->_loopcenter = x->_samplength / 2.0;
	} else {
		x->_loopstart = x->_loopcenter - (f / 2.0);
		x->_loopend = x->_loopcenter + (f / 2.0);
		if(x->_loopstart < 0)
		{
			x->_loopstart = 0.0;
			x->_loopend = f;
			x->_loopcenter = f / 2.0;
		} else if(x->_loopend > x->_samplength) {
			x->_loopend = x->_samplength;
			x->_loopstart = x->_loopend - f;
			x->_loopcenter = x->_loopstart + (f / 2.0);
		}
	}
	lp_bang(x);
}

void lp_samplength(t_lppts *x, t_ms f)
{
	x->_samplength = f;
	if(x->_loopend > x->_samplength)
	{
		x->_loopend = x->_samplength;
		if(x->_loopstart > x->_loopend)
			x->_loopstart = x->_loopend;
		x->_looplength = x->_loopend - x->_loopstart;
		x->_loopcenter = x->_loopstart + (x->_looplength / 2.0);
	}
	lp_bang(x);
}

t_max_err lp_setloopstart(t_lppts *x, void *attr, long argc, t_atom *argv)
{
	if(argc == 1 && argv != NULL)
		lp_loopstart(x, atom_getfloat(argv));
	return MAX_ERR_NONE;
}

t_max_err lp_setloopend(t_lppts *x, void *attr, long argc, t_atom *argv)
{
	if(argc == 1 && argv != NULL)
		lp_loopend(x, atom_getfloat(argv));
	return MAX_ERR_NONE;
}

t_max_err lp_setloopcenter(t_lppts *x, void *attr, long argc, t_atom *argv)
{
	if(argc == 1 && argv != NULL)
		lp_loopcenter(x, atom_getfloat(argv));
	return MAX_ERR_NONE;
}

t_max_err lp_setlooplength(t_lppts *x, void *attr, long argc, t_atom *argv)
{
	if(argc == 1 && argv != NULL)
		lp_looplength(x, atom_getfloat(argv));
	return MAX_ERR_NONE;
}

t_max_err lp_setsamplength(t_lppts *x, void *attr, long argc, t_atom *argv)
{
	if(argc == 1 && argv != NULL)
		lp_samplength(x, atom_getfloat(argv));
	return MAX_ERR_NONE;
}

